<?php

namespace app\api\controller;

use app\service\modbus\ModbusTcpService;
use app\service\plc\PlcAccess;
use Exception;
use ModbusTcpClient\Network\BinaryStreamConnection;
use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersRequest;
use ModbusTcpClient\Packet\ModbusFunction\ReadHoldingRegistersResponse;
use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersRequest;
use ModbusTcpClient\Packet\ModbusFunction\ReadInputRegistersResponse;
use ModbusTcpClient\Packet\ModbusFunction\WriteMultipleRegistersRequest;
use ModbusTcpClient\Packet\ResponseFactory;
use ModbusTcpClient\Packet\RtuConverter;
use ModbusTcpClient\Utils\Endian;
use ModbusTcpClient\Utils\Packet;
use support\Response;

class ModbusTcpController
{
    /**
     * 获取ModbusTcp值
     * @return Response
     */
    public function getModbusTcpValue(): Response
    {
        $dd = new ModbusTcpService();
        //写入值
        $arr_write=[
            ['start_address'=>1,'get_type'=>'int16','value'=>bindec('0110011000000000')],
            ['start_address'=>4,'get_type'=>'int16','value'=>'1'],
            ['start_address'=>5,'get_type'=>'int16','value'=>'2'],
            ['start_address'=>6,'get_type'=>'int16','value'=>'13'],
            ['start_address'=>7,'get_type'=>'int16','value'=>'4'],
            ['start_address'=>8,'get_type'=>'int16','value'=>'5'],
            ['start_address'=>9,'get_type'=>'int16','value'=>'6'],
            ['start_address'=>10,'get_type'=>'int16','value'=>'7'],
            ['start_address'=>11,'get_type'=>'int16','value'=>'18'],
        ];
        $dd->getModbusTcpTypeWrite($arr_write);
        //读取值
        $arr=[
            ['start_address'=>1,'get_type'=>'bit','bit'=>10,'name'=>'温度开关'],
            ['start_address'=>2,'get_type'=>'bit','bit'=>5,'name'=>'湿度开关'],
            ['start_address'=>3,'get_type'=>'byte','byte'=>true,'name'=>'温度值'],
            ['start_address'=>4,'get_type'=>'int16','name'=>'湿度值00'],
            ['start_address'=>5,'get_type'=>'int16','name'=>'湿度值01'],
            ['start_address'=>6,'get_type'=>'int16','name'=>'湿度值02'],
            ['start_address'=>7,'get_type'=>'int16','name'=>'湿度值03'],
            ['start_address'=>8,'get_type'=>'int16','name'=>'湿度值04'],
            ['start_address'=>9,'get_type'=>'int16','name'=>'湿度值05'],
            ['start_address'=>10,'get_type'=>'int16','name'=>'湿度值06'],
            ['start_address'=>11,'get_type'=>'int16','name'=>'湿度值07'],
        ];
        $data = $dd->getModbusTcpTypeRead($arr);

        $arr1=[
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>1,'get_type'=>'bit','bit'=>1,'name'=>'温度开关'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>1,'get_type'=>'bit','bit'=>10,'name'=>'湿度开关'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>3,'get_type'=>'byte','byte'=>true,'name'=>'温度值'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>4,'get_type'=>'int16','name'=>'湿度值00'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>5,'get_type'=>'int16','name'=>'湿度值01'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>6,'get_type'=>'int16','name'=>'湿度值02'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>7,'get_type'=>'int16','name'=>'湿度值03'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>8,'get_type'=>'int16','name'=>'湿度值04'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>9,'get_type'=>'int16','name'=>'湿度值05'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>10,'get_type'=>'int16','name'=>'湿度值06'],
            ['uri'=>'tcp://127.0.0.1:502','start_address'=>11,'get_type'=>'int16','name'=>'湿度值07'],
        ];
        //$data = $dd->getModbusTcpTypeMultipleRead($arr1);
        return successMessage("对对对", $data);
        Endian::$defaultEndian = Endian::BIG_ENDIAN_LOW_WORD_FIRST; // set default (global) endian used for parsing data
        $connection = BinaryStreamConnection::getBuilder()
            ->setPort(502)
            ->setHost('127.0.0.1')
            ->setConnectTimeoutSec(1.5) // timeout when establishing connection to the server
            ->setWriteTimeoutSec(0.5) // timeout when writing/sending packet to the server
            ->setReadTimeoutSec(0.3) // timeout when waiting response from server
            ->build();
        $data = [];
        for ($i = 0; $i <= 10; $i++) {
            $startAddress = $i;
            $quantity = 1;
            //站号
            $unitID = 1;
            try {
                $packet = new ReadHoldingRegistersRequest($startAddress, $quantity, $unitID); // NB: This is Modbus TCP packet not Modbus RTU over TCP!
                $binaryData = $connection->connect()->sendAndReceive($packet);
                /**
                 * @var $response ReadHoldingRegistersResponse
                 */
                $response = ResponseFactory::parseResponseOrThrow($binaryData);
                foreach ($response as $word) {
                    $data[] = [
                        'point' => $i,
                        'value' => $word->getBytes()[1]
                    ];
                }
            } catch (Exception $exception) {
                return errorMessage('An exception occurred' . $exception->getMessage() . $exception->getTraceAsString());
            } finally {
                $connection->close();
            }
        }
        return successMessage("对对对", $data);
    }

    /**
     * 设置ModbusTcp值
     * @return Response
     */
    public function setModbusTcpValue(): Response
    {
        $point = request()->get('point', 0);
        $value = request()->get('value', 0);
        $connection = BinaryStreamConnection::getBuilder()
            ->setPort(503)
            ->setHost('127.0.0.1')
            ->setConnectTimeoutSec(1.5) // timeout when establishing connection to the server
            ->setWriteTimeoutSec(0.5) // timeout when writing/sending packet to the server
            ->setReadTimeoutSec(0.3) // timeout when waiting response from server
            ->build();
        $data_value = modbus_data_type($value);
        if (empty($data_value['value'])) {
            return errorMessage("数据类型错误");
        }
        if ($data_value['error']) {
            return errorMessage($data_value['error']);
        }
        $v = $data_value['value'];
        try {
            $packet1 = new WriteMultipleRegistersRequest($point, [$v], 1);
            $binaryData = $connection->connect()->sendAndReceive($packet1);
        } catch (Exception $exception) {
            return errorMessage('An exception occurred' . $exception->getMessage() . $exception->getTraceAsString());
        } finally {
            $connection->close();
        }
        //unpack('H*', $binaryData)[1])
        return successMessage("对对对", [unpack('H*', $binaryData)[1]]);
    }


    //获取ModbusRtu值
    public function getModbusRtuValue()
    {
        $connection = BinaryStreamConnection::getBuilder()
            ->setPort(502)
            ->setHost('127.0.0.1')
            ->build();

        $data = [];
        $startAddress = 3;
        $quantity = 1;
        //站号
        $unitID = 1;
        try {
            $packet = new ReadHoldingRegistersRequest($startAddress, $quantity, $unitID); // NB: This is Modbus TCP packet not Modbus RTU over TCP!
            $binaryData = $connection->connect()->sendAndReceive($packet);
            /**
             * @var $response ReadHoldingRegistersResponse
             */
            $response = ResponseFactory::parseResponseOrThrow($binaryData);
            foreach ($response as $word) {
                $data[] = [
                    'point' => 1,
                    'value' => $word->getBytes()[1]
                ];
            }
        } catch (Exception $exception) {
            return errorMessage('An exception occurred' . $exception->getMessage() . $exception->getTraceAsString());
        } finally {
            $connection->close();
        }
        return successMessage("对对对", $data);
    }

    //废
    public function getRtu()
    {
        $connection = BinaryStreamConnection::getBuilder()
            ->setUri('COM1')
            //->setProtocol('serial')
            ->setIsCompleteCallback(static function ($binaryData, $streamIndex): bool {
                return Packet::isCompleteLengthRTU($binaryData);
            })->build();

        $startAddress = 1;
        $quantity = 2;
        $slaveId = 1;
        $tcpPacket = new ReadInputRegistersRequest($startAddress, $quantity, $slaveId);
        $rtuPacket = RtuConverter::toRtu($tcpPacket);
        try {
            $data = unpack('H*', $rtuPacket)[1];
            //return successMessage('操作成功',[$data]);
            $binaryData = $connection->connect()->sendAndReceive($rtuPacket);

            echo 'RTU Binary received (in hex):   ' . unpack('H*', $binaryData)[1] . PHP_EOL;
            /** @var ReadInputRegistersResponse $response */
            $response = RtuConverter::fromRtu($binaryData)->withStartAddress($startAddress);
            echo 'Parsed packet (in hex):     ' . $response->toHex() . PHP_EOL;
            echo PHP_EOL;
            echo 'Temperature: ' . ($response->getWordAt(1)->getInt16() / 10) . PHP_EOL;
            echo 'Humidity: ' . ($response->getWordAt(2)->getInt16() / 10) . PHP_EOL;
        } catch (Exception $exception) {
           return errorMessage($exception->getMessage().$exception->getTraceAsString());
        } finally {
            $connection->close();
        }
    }
}
